/*
* INSANE - Interactive Structural Analysis Environment
*
* Copyright (C) 2003-2005
* Universidade Federal de Minas Gerais
* Escola de Engenharia
* Departamento de Engenharia de Estruturas
* 
* Author's email :    insane@dees.ufmg.br
* Author's website :  http://www.dees.ufmg.br/insane
* 
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or any later version.
* 
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
* 
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/
package br.ufmg.dees.insane.ui.rich.full.command;

import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;

import javax.swing.JInternalFrame;
import javax.swing.JOptionPane;

import br.ufmg.dees.insane.commons.command.Command;
import br.ufmg.dees.insane.commons.controller.Controller;
import br.ufmg.dees.insane.model.geo.GCubicCurve;
import br.ufmg.dees.insane.model.geo.GCurve;
import br.ufmg.dees.insane.model.geo.GLine;
import br.ufmg.dees.insane.model.geo.GPoint;
import br.ufmg.dees.insane.model.geo.GQuadCurve;
import br.ufmg.dees.insane.model.geo.GRegion;
import br.ufmg.dees.insane.model.geo.GeometricModel;
import br.ufmg.dees.insane.prep.BilinearToLagranQ;
import br.ufmg.dees.insane.prep.BilinearToLagranT;
import br.ufmg.dees.insane.prep.BilinearToSerenQ;
import br.ufmg.dees.insane.prep.BilinearToSerenT;
import br.ufmg.dees.insane.prep.LoftingToLagranQ;
import br.ufmg.dees.insane.prep.LoftingToLagranT;
import br.ufmg.dees.insane.prep.LoftingToSerenQ;
import br.ufmg.dees.insane.prep.LoftingToSerenT;
import br.ufmg.dees.insane.prep.MapElement;
import br.ufmg.dees.insane.prep.MapNode;
import br.ufmg.dees.insane.prep.Mapping;
import br.ufmg.dees.insane.prep.MappingModel;
import br.ufmg.dees.insane.prep.TrilinearToLagran;
import br.ufmg.dees.insane.prep.TrilinearToSeren;
import br.ufmg.dees.insane.ui.rich.full.InsaneInternalInterface;
import br.ufmg.dees.insane.ui.rich.full.controller.MappingController;
import br.ufmg.dees.insane.ui.rich.full.dialogs.ElementTypeDialog;

/**
 *
 * @author Pitangueira, Roque & Brugiolo, Marco & Fonseca, Flavio
 * @version 0.0.1 Beta
 */
public class AddMeshCommand implements Command, Cloneable {
    
	private InsaneInternalInterface parent;
	private Controller controller;
	private Mapping map;
	private MappingModel mmodel;
	private GeometricModel gmodel;
	int lastNode;
	int lastElement;
	
//*****************************************************************************
	
	/** Creates new AddMeshCommand */
    public AddMeshCommand(JInternalFrame parent) {
		this.parent = (InsaneInternalInterface)parent;
		controller = this.parent.getController();
	}
	
//*****************************************************************************
	
	public void execute() {
		
		//Adding parametric elements
		boolean free = false;
		boolean problem = false;
		boolean showDialog = false;
		ListIterator regions;
		GRegion region;
		List curves;
		String eTypeLS, eTypeLS2;
		String eTypeTQ, eTypeTQ2;
		boolean linearD, linearD2;
		int nPointsPerElement, nPointsPerElement2;
		int nElementsTransverseDirection, nElementsTransverseDirection2;
		eTypeLS = "Serendipity";
		eTypeTQ = "Quadrilateral";
		linearD = false;
		nPointsPerElement = 4;
		nElementsTransverseDirection = 1;
		ElementTypeDialog dlg;
		ArrayList rgs = controller.getSelection();
		if ( rgs.size() < 1 ) {
			showDialog = true;
			controller.removeAll();
			rgs = ((MappingController)controller).getRegions();
		}
		regions = rgs.listIterator();
		if (regions.hasNext()) {
			region = (GRegion)regions.next();
			curves = region.getCurves();
			
			if ( curves.size()==3 )
				nPointsPerElement = 3;
			
			dlg = new ElementTypeDialog(parent, curves.size(), eTypeLS, eTypeTQ, linearD, nPointsPerElement, nElementsTransverseDirection, region.getLabel() );
			dlg.showDialog();
			eTypeLS2 = dlg.getETypeLS();
			eTypeTQ2 = dlg.getETypeTQ();
			linearD2 = dlg.getLinearDivisor();
			nPointsPerElement2 = dlg.getETypeNN();
			nElementsTransverseDirection2 = dlg.getETypeNE();
			
			String message, message1, message2;
			GCurve curve;
			int np;
			lastNode = ((MappingController)controller).getLastNode();
			lastElement = ((MappingController)controller).getLastElement();
			
			regions = rgs.listIterator();
			while(regions.hasNext()) {
				region = (GRegion)regions.next();
				message1 = "R" + region.getLabel();
				message = "R" + region.getLabel() + ": ";
				
				message2 = parent.getResourceBundle().getString("cmMesh01") + " " + message1;
				parent.addOutputText(message2);
				
				curves = region.getCurves();
				ListIterator cvs = curves.listIterator();
				while(cvs.hasNext()) {
					curve = (GCurve)cvs.next();
					message = message + "C" + curve.getLabel() + ", ";
				}
				
				int ncvs = curves.size();
				int [] nparts = new int[4];
				int j = 0;
				cvs = curves.listIterator();
				while(cvs.hasNext()) {
					curve = (GCurve)cvs.next();
					message = message + "\n      " + "C" + curve.getLabel();
					message2 = "";
					
					np = 0;
					List prs = curve.getPrimitives();
					ListIterator primitives = prs.listIterator();
					while(primitives.hasNext()) {
						Object primitive = primitives.next();
						if (primitive instanceof GPoint) {
							GPoint prt = (GPoint)primitive;
							message2 = message2 + "P" + prt.getLabel()+ " {" + prt.getNParts() + "}, ";
							np = np + prt.getNParts();
						}
						if (primitive instanceof GLine) {
							GLine prt = (GLine)primitive;
							message2 = message2 + "P" + prt.getLabel()+ " {" + prt.getNParts() + "}, ";
							np = np + prt.getNParts();
						}
						if (primitive instanceof GQuadCurve) {
							GQuadCurve prt = (GQuadCurve)primitive;
							message2 = message2 + "P" + prt.getLabel()+ " {" + prt.getNParts() + "}, ";
							np = np + prt.getNParts();
						}
						if (primitive instanceof GCubicCurve) {
							GCubicCurve prt = (GCubicCurve)primitive;
							message2 = message2 + "P" + prt.getLabel()+ " {" + prt.getNParts() + "}, ";
							np = np + prt.getNParts();
						}
					}
					message = message + " {" + np + parent.getResourceBundle().getString("cmMesh02") + " " + message2;
					nparts[j] = np;
					j++;
				}
				
				boolean consistent = true;
				if (ncvs < 4) {
					if (nparts[0] != nparts[1]) { consistent = false; }
				}
				if (ncvs > 2) {
					if (nparts[0] != nparts[2]) { consistent = false; }
				}
				if (ncvs == 4) {
					if (nparts[1] != nparts[3]) { consistent = false; }
				}
				
				if (consistent) {
					GRegion rg = region;
					GPoint[] curve1 = ((GCurve)curves.get(0)).getDiscrete((GPoint)rg.getFirstPoint());
					GPoint[] curve2 = ((GCurve)curves.get(1)).getDiscrete(curve1[curve1.length-1]);
					int nPoints1 = curve1.length;
					int nPoints2 = curve2.length;
					
					eTypeLS = eTypeLS2;
					eTypeTQ = eTypeTQ2;
					linearD = linearD2;
					nPointsPerElement = nPointsPerElement2;
					nElementsTransverseDirection = nElementsTransverseDirection2;
					
					int npes;
					
					boolean inconsistent = true;
					while ( inconsistent ) {
						if ((showDialog || problem) && free) {
							dlg = new ElementTypeDialog(parent, curves.size(), eTypeLS, eTypeTQ, linearD,  nPointsPerElement, nElementsTransverseDirection, rg.getLabel() );
							dlg.showDialog();
							eTypeLS = dlg.getETypeLS();
							eTypeTQ = dlg.getETypeTQ();
							linearD = dlg.getLinearDivisor();
							nPointsPerElement = dlg.getETypeNN();
							nElementsTransverseDirection = dlg.getETypeNE();
						}
						free = true;
						problem = false;
						if ( curves.size()==3 )
							eTypeTQ = "Triangulate";
						
						inconsistent = false;
						if ( eTypeTQ == "Triangulate" ) {
							if (nPointsPerElement<3) {
								nPointsPerElement=3;
								inconsistent = true;
							}
						} else {
							if (nPointsPerElement<4) {
								nPointsPerElement=4;
								inconsistent = true;
							}
						}
						if ( eTypeLS=="Serendipity" ) {
							if ( eTypeTQ=="Triangulate" ) {
								if ( (nPointsPerElement%3)!=0 )
									inconsistent = true;
								npes = nPointsPerElement/3+1;
								nPointsPerElement = (npes-1)*3;
								while ( ((nPoints1-1)%(npes-1))!=0 || ((nPoints2-1)%(npes-1))!=0 ) {
									nPointsPerElement = ((--npes)-1)*3;
									inconsistent = true;
								}
							} else {
								if ( (nPointsPerElement%4)!=0 )
									inconsistent = true;
								npes = nPointsPerElement/4+1;
								nPointsPerElement = (npes-1)*4;
								while ( ((nPoints1-1)%(npes-1))!=0 || ((nPoints2-1)%(npes-1))!=0 ) {
									nPointsPerElement = ((--npes)-1)*4;
									inconsistent = true;
								}
							}
						} else {
							if ( eTypeTQ=="Triangulate" ) {
								int aux1 = nPointsPerElement;
								int aux2 = 0;
								int aux3 = 0;
								while ( aux1 > 1 ) {
									aux2++;
									aux1 = aux1 - aux2;
									aux3 = aux3 + aux2;
								}
								if ( aux1 != 0 )
									inconsistent = true;
								npes = aux2;
								nPointsPerElement = aux3;
								while ( ((nPoints1-1)%(npes-1))!=0 || ((nPoints2-1)%(npes-1))!=0 ) {
									nPointsPerElement = nPointsPerElement - npes;
									npes--;
									inconsistent = true;
								}
							} else {
								if ( ((Math.sqrt(nPointsPerElement))%1)!=0 )
									inconsistent = true;
								npes = (int)Math.sqrt(nPointsPerElement);
								nPointsPerElement = npes*npes;
								while ( ((nPoints1-1)%(npes-1))!=0 || ((nPoints2-1)%(npes-1))!=0 ) {
									nPointsPerElement = (--npes)*(npes);
									inconsistent = true;
								}
							}
						}
						if (inconsistent) problem = true;
					}
					
					if (curves.size() == 2) {
						if ( eTypeLS == "Serendipity" ) {
							if ( eTypeTQ == "Triangulate" ) {
								map = new LoftingToSerenT(lastNode, lastElement, nPointsPerElement, nElementsTransverseDirection, linearD, rg);
							}
							if ( eTypeTQ == "Quadrilateral" ) {
								map = new LoftingToSerenQ(lastNode, lastElement, nPointsPerElement, nElementsTransverseDirection, rg);
							}
						}
						if ( eTypeLS == "Lagrange" ) {
							if ( eTypeTQ == "Triangulate" ) {
								map = new LoftingToLagranT(lastNode, lastElement, nPointsPerElement, nElementsTransverseDirection, linearD, rg);
							}
							if ( eTypeTQ == "Quadrilateral" ) {
								map = new LoftingToLagranQ(lastNode, lastElement, nPointsPerElement, nElementsTransverseDirection, rg);
							}
						}
					}
					if (curves.size() == 3) {
						if ( eTypeLS == "Serendipity" ) {
							map = new TrilinearToSeren(lastNode, lastElement, nPointsPerElement, rg);
						}
						if ( eTypeLS == "Lagrange" ) {
							map = new TrilinearToLagran(lastNode, lastElement, nPointsPerElement, rg);
						}
					}
					if (curves.size() == 4) {
						if ( eTypeLS == "Serendipity" ) {
							if ( eTypeTQ == "Triangulate" ) {
								map = new BilinearToSerenT(lastNode, lastElement, nPointsPerElement, linearD, rg);
							}
							if ( eTypeTQ == "Quadrilateral" ) {
								map = new BilinearToSerenQ(lastNode, lastElement, nPointsPerElement, rg);
							}
						}
						if ( eTypeLS == "Lagrange" ) {
							if ( eTypeTQ == "Triangulate" ) {
								map = new BilinearToLagranT(lastNode, lastElement, nPointsPerElement, linearD, rg);
							}
							if ( eTypeTQ == "Quadrilateral" ) {
								map = new BilinearToLagranQ(lastNode, lastElement, nPointsPerElement, rg);
							}
						}
					}
					
					ListIterator nnd = (map.getNodes()).listIterator();
					while(nnd.hasNext()) {
						controller.add(nnd.next());
					}
					lastNode = lastNode + map.getNodes().size();
					((MappingController)controller).setLastNode(lastNode);
					
					ArrayList newElements = (ArrayList)map.getElements();
					ListIterator nne = newElements.listIterator();
					while(nne.hasNext()) {
						controller.add(nne.next());
					}
					lastElement = lastElement + map.getElements().size();
					rg.setElements(newElements);
					((MappingController)controller).setLastElement(lastElement);
					
					parent.getDrawing().repaint();
				} else {
					message = message + "\n" + parent.getResourceBundle().getString("cmMesh03");
					JOptionPane.showMessageDialog(null, message, "INSANE",JOptionPane.ERROR_MESSAGE);
				}
			}
			((MappingController)controller).removeCoincidentNodes();
			((MappingController)controller).setNumber();
			controller.unSelectAll();
		}
		
		//Adding frame elements
		mmodel = parent.getModel().getMModel();
		
		gmodel = (GeometricModel)parent.getModel().getGModel();
		MapNode node;
		MapNode[] nds = new MapNode[2];
		MapElement elm;
		GLine gl;
		GQuadCurve gq;
		GCubicCurve gc;
		
		//Adding nodes to FemModel
/*		ListIterator pts = gmodel.getPoints().listIterator();
		while (pts.hasNext()){
			p = (Point2D)pts.next();
			node = new MapNode(p.getX(), p.getY());
			mmodel.add(node);
		}
*/		
		//Adding nodes and elements from lines partition to FemModel
		ListIterator lns = gmodel.getLines().listIterator();
		while (lns.hasNext()) {
			gl = (GLine)lns.next();
			if (gl.isFrame()) {
				int nParts = gl.getNParts();
				GPoint[] disc;
				disc = gl.getDiscrete();
				for (int count = 1; count < nParts; count++) {
					node = new MapNode(disc[count].getX(), disc[count].getY());
					mmodel.add(node);
				}
				for (int count = 0; count < nParts; count++) {
					nds[0] = mmodel.getMapNode(disc[count].getX(), disc[count].getY());
					nds[1] = mmodel.getMapNode(disc[count+1].getX(), disc[count+1].getY());
					elm = new MapElement(nds);
					elm.setType('F', 'F');
					mmodel.add(elm);
				}
			}
		}
		
		//Adding nodes and elements from cubics curves partition to FemModel
		ListIterator cbs = gmodel.getCubics().listIterator();
		while(cbs.hasNext()) {
			gc = (GCubicCurve)cbs.next();
			if (gc.isFrame()) {
				int nParts = gc.getNParts();
				GPoint[] disc;
				disc = gc.getDiscrete();
				for (int count = 1; count < nParts; count++) {
					node = new MapNode(disc[count].getX(), disc[count].getY());
					mmodel.add(node);
				}
				for (int count = 0; count < nParts; count++) {
					nds[0] = mmodel.getMapNode(disc[count].getX(), disc[count].getY());
					nds[1] = mmodel.getMapNode(disc[count+1].getX(), disc[count+1].getY());
					elm = new MapElement(nds);
					elm.setType('F', 'F');
					mmodel.add(elm);
				}
			}
		}
		
		//Adding nodes and elements from quadratic curves partition to FemModel
		ListIterator qcs = gmodel.getQuads().listIterator();
		while(qcs.hasNext()) {
			gq = (GQuadCurve)qcs.next();
			if (gq.isFrame()) {
				int nParts = gq.getNParts();
				GPoint[] disc;
				disc = gq.getDiscrete();
				for (int count = 1; count < nParts; count++) {
					node = new MapNode(disc[count].getX(), disc[count].getY());
					mmodel.add(node);
				}
				for (int count = 0; count < nParts; count++) {
					nds[0] = mmodel.getMapNode(disc[count].getX(), disc[count].getY());
					nds[1] = mmodel.getMapNode(disc[count+1].getX(), disc[count+1].getY());
					elm = new MapElement(nds);
					elm.setType('F', 'F');
					mmodel.add(elm);
				}
			}
		}
		
		this.nameNodes();
		this.nameElements();
		
	}
	
//*****************************************************************************
	
	public void undo() {}
    
//*****************************************************************************
	
	public void redo() {}
	
//*****************************************************************************

	/** Sets the labels of the Nodes. */
	public void nameNodes() {
		
		ListIterator nodes = mmodel.getMapNodes().listIterator();
		MapNode node;
		int i = 0;
		int name;
		while (nodes.hasNext()) {
			node = (MapNode)nodes.next();
			i++;
			name = i;
			node.setLabel(name);
		}
		
	}
	
//*****************************************************************************
	
	/** Sets the labels of the Elements. */
	public void nameElements() {
		
		ListIterator elms = mmodel.getMapElements().listIterator();
		MapElement elm;
		//MapNode[] nds;
		int name;
		int i =0;
		while (elms.hasNext()) {
			elm = (MapElement)elms.next();
			i++;
			//nds = elm.getBoundary();
			//name = nds[0].getLabel()*100 + nds[1].getLabel();
			name = i;
			elm.setLabel(name);
		}
		
	}
	
//*****************************************************************************
}

